Avoid visiting deps too often in resolve
authorAlex Crichton <alex@alexcrichton.com>
Fri, 17 Oct 2014 19:23:10 +0000 (12:23 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 22 Oct 2014 18:29:52 +0000 (11:29 -0700)
If we're activating an already-active version of a dependency, then there's no
need to actually recurse into it. Instead, we can skip it if we're not enabling
any extra features in it to avoid extraneous recursion.

src/cargo/core/resolver/mod.rs
tests/resolve.rs
tests/test_cargo_compile.rs

index 34458472860976298402d8b64a55a6504d51dca3..8909d1d82bd9d82b6023ef311544d8ebaecbff25 100644 (file)
@@ -221,35 +221,47 @@ fn activate_deps<R: Registry>(cx: Context,
         log!(5, "{}[{}]>{} trying {}", parent.get_name(), cur, dep.get_name(),
              candidate.get_version());
         let mut my_cx = cx.clone();
-        {
+        let early_return = {
             my_cx.resolve.graph.link(parent.get_package_id().clone(),
                                      candidate.get_package_id().clone());
             let prev = match my_cx.activations.entry(key.clone()) {
                 Occupied(e) => e.into_mut(),
                 Vacant(e) => e.set(Vec::new()),
             };
-            if !prev.iter().any(|c| c == candidate) {
+            if prev.iter().any(|c| c == candidate) {
+                match cx.resolve.features(candidate.get_package_id()) {
+                    Some(prev_features) => {
+                        features.iter().all(|f| prev_features.contains(f))
+                    }
+                    None => features.len() == 0,
+                }
+            } else {
                 my_cx.resolve.graph.add(candidate.get_package_id().clone(), []);
                 prev.push(candidate.clone());
+                false
             }
-        }
+        };
 
-        // Dependency graphs are required to be a DAG. Non-transitive
-        // dependencies (dev-deps), however, can never introduce a cycle, so we
-        // skip them.
-        if dep.is_transitive() &&
-           !cx.visited.borrow_mut().insert(candidate.get_package_id().clone()) {
-            return Err(human(format!("cyclic package dependency: package `{}` \
-                                      depends on itself",
-                                     candidate.get_package_id())))
-        }
-        let my_cx = try!(activate(my_cx, registry, &**candidate, method));
-        if dep.is_transitive() {
-            cx.visited.borrow_mut().remove(candidate.get_package_id());
-        }
-        let my_cx = match my_cx {
-            Ok(cx) => cx,
-            Err(e) => { last_err = Some(e); continue }
+        let my_cx = if early_return {
+            my_cx
+        } else {
+            // Dependency graphs are required to be a DAG. Non-transitive
+            // dependencies (dev-deps), however, can never introduce a cycle, so we
+            // skip them.
+            if dep.is_transitive() &&
+               !cx.visited.borrow_mut().insert(candidate.get_package_id().clone()) {
+                return Err(human(format!("cyclic package dependency: package `{}` \
+                                          depends on itself",
+                                         candidate.get_package_id())))
+            }
+            let my_cx = try!(activate(my_cx, registry, &**candidate, method));
+            if dep.is_transitive() {
+                cx.visited.borrow_mut().remove(candidate.get_package_id());
+            }
+            match my_cx {
+                Ok(cx) => cx,
+                Err(e) => { last_err = Some(e); continue }
+            }
         };
         match try!(activate_deps(my_cx, registry, parent, deps, cur + 1)) {
             Ok(cx) => return Ok(Ok(cx)),
index aa07288197d66372858ecc370b6fdc62193cc70f..30c8c04fca857e5e17cf5d6d863f3de6571d6d04 100644 (file)
@@ -345,13 +345,7 @@ fn resolving_cycle() {
         pkg!("foo" => ["foo"]),
     ));
 
-    let res = resolve(pkg_id("root"), vec![
+    let _ = resolve(pkg_id("root"), vec![
         dep_req("foo", "1"),
     ], &mut reg);
-    assert!(res.is_err());
-
-    assert_eq!(res.to_string().as_slice(), "Err(\
-cyclic package dependency: package `foo v1.0.0 (registry http://example.com/)` \
-depends on itself\
-)");
 }
index 83164d13c0a7a597c1b26d732751ca93f488e2e4..d55b2e0afb9e7dc777909da91701f33f40bbd0dd 100644 (file)
@@ -1091,9 +1091,7 @@ test!(self_dependency {
         "#)
         .file("src/test.rs", "fn main() {}");
     assert_that(p.cargo_process("build"),
-                execs().with_status(101).with_stderr("\
-cyclic package dependency: package `test v0.0.0 ([..])` depends on itself
-"));
+                execs().with_status(0));
 })
 
 #[cfg(not(windows))]